/*
 * Copyright © 2004-2014 chenYuan. All rights reserved.
 * @Website:wwww.jspx.net
 * @Mail:39793751@qq.com
 * @author: chenYuan , 陈原
 * @License: Jspx.net Framework Code is open source (LGPL)，Jspx.net Framework 使用LGPL 开源授权协议发布。
 * @jvm:jdk1.6+  x86/amd64
 *
 */
package com.jspx.utils;

import java.lang.reflect.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.*;
import java.io.*;
import java.net.URL;


/**
 * 涉及类的方法等的工具类。
 *
 * @since 0.4
 */

public class ClassUtil {
    public static final String methodName_set = "set";
    public static final String methodName_get = "get";
    public static final String methodName_is = "is";
    /**
     * 私有构造方法，防止类的实例化，因为工具类不需要实例化。
     */
    private ClassUtil() {
    }

    public static boolean isNumberType(Type cla) {
        return cla.equals(long.class) || cla.equals(Long.class) || cla.equals(int.class) || cla.equals(Integer.class) ||
                cla.equals(float.class) || cla.equals(Float.class) || cla.equals(double.class) || cla.equals(Double.class) ||
                cla.equals(BigDecimal.class);
    }

    @SuppressWarnings("unchecked")
    public static boolean isStandardProperty(Class clazz) {
        return clazz.isPrimitive() || isNumberProperty(clazz) || clazz.isAssignableFrom(Byte.class) || clazz.isAssignableFrom(Character.class)
                || clazz.isAssignableFrom(String.class) || clazz.isAssignableFrom(char.class) || clazz.isAssignableFrom(Boolean.class) ||
                clazz.isAssignableFrom(Date.class) || clazz.isAssignableFrom(Timestamp.class);
    }


    public static boolean isNumberProperty(Class clazz) {
        if (clazz == null) return false;
        return clazz.isAssignableFrom(short.class) ||
                clazz.isAssignableFrom(Short.class) ||
                clazz.isAssignableFrom(int.class) ||
                clazz.isAssignableFrom(Integer.class) ||
                clazz.isAssignableFrom(long.class) ||
                clazz.isAssignableFrom(Long.class) ||
                clazz.isAssignableFrom(float.class) ||
                clazz.isAssignableFrom(Float.class) ||
                clazz.isAssignableFrom(double.class) ||
                clazz.isAssignableFrom(Double.class) ||
                clazz.isAssignableFrom(BigDecimal.class);
    }


    public static boolean isJacMethod(String methodName) {
        return methodName != null && methodName.startsWith("_");
    }

      /**
     * @param c    类
     * @param name 方法
     * @return 是否有此方法
     */
    public static boolean haveMethodsName(Class c, String name) {
        try {
            Method[] methods = c.getMethods();
            for (Method method : methods) {
                if (!(Modifier.isStatic(method.getModifiers()) || isJacMethod(method.getName()))) {
                    String aMethodsName = method.getName();
                    if (aMethodsName == null) continue;
                    if (aMethodsName.equals(name)) {
                        return true;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }


    /**
     * @param c    存在方法
     * @param name 方法
     * @return 得到Set 方法的 key方法明  和 value 类型
     */
    public static Map<String, Class> getMethodsNameAndType(Class c, String name) {
        Map<String, Class> result = new HashMap<String, Class>();
        try {
            Method[] methods = c.getMethods();
            for (Method method : methods) {
                if (!(Modifier.isStatic(method.getModifiers()) || isJacMethod(method.getName()))) {
                    String mName = method.getName();
                    if (mName == null) continue;
                    if (methodName_set.equals(name) && mName.startsWith(name)) {
                        result.put(StringUtil.uncapitalize(mName.substring(name.length())), method.getParameterTypes()[0]);
                    } else if (methodName_get.equals(name) && mName.startsWith(name)) {
                        result.put(StringUtil.uncapitalize(mName.substring(name.length())), method.getReturnType());
                    } else if (methodName_is.equals(name) && mName.startsWith(name)) {
                        result.put(StringUtil.uncapitalize(mName.substring(name.length())), method.getReturnType());
                    }

                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 得到所有方法命
     *
     * @param cl 类
     * @return String[]
     */
    public static String[] getAllMethodsName(Class cl) {

        String[] methodNames = null;
        List<String> tmp = new ArrayList<String>();
        try {
            Method[] methods = cl.getMethods();
            for (Method method : methods) {
                if (!(Modifier.isStatic(method.getModifiers()) || isJacMethod(method.getName()))) {
                    tmp.add(method.getName());
                }
            }
            methodNames = new String[tmp.size()];
            for (int i = 0; i < tmp.size(); i++) {
                methodNames[i] = tmp.get(i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return methodNames;
    }


    /**
     * 支持 两种写法
     * boolean b = ClassUtil.isSetMethod(MatterView.class,"setRequest");
     * boolean b2 = ClassUtil.isSetMethod(MatterView.class,"request");
     *
     * @param c     类名称
     * @param mName 方法名称
     * @return 判断是否有set方法
     */
    public static boolean isSetMethod(Class c, String mName) {
        if (mName == null) return false;
        Method[] methods = getDeclaredMethods(c);
        for (Method method : methods) {

            if (method.getName().equals(mName) && method.getName().startsWith(methodName_set)) return true;
            if (StringUtil.uncapitalize(method.getName().substring(methodName_set.length())).equals(mName) && method.getName().startsWith(methodName_set))
                return true;
        }
        return false;
    }

    /**
     * 判断一个对象数组是否是同质数组。
     * 这里的同质是严格的同质，即他们的实际类类型必须完全相同。
     *
     * @param objects 要比较的对象数组
     * @return 所有元素的类类型完全相同时返回true，如果数组中只有一个元素时也返回true，否则返回false
     * @since 0.5

    public static boolean isSameClassType(Object[] objects)
    {
    if (objects.length == 1) return true;
    Class c = objects[0].getClass();
    for (int i = 1; i < objects.length; i++)
    {
    if (!c.equals(objects[i].getClass()))
    {
    return false;
    }
    }
    return true;
    }
     */

    /**
     * 判断一个对象数组是否是同质数组且是指定的类类型。
     * 这里的同质是严格的同质，即他们的实际类类型必须完全相同。
     *
     * @param objects 要比较的对象数组
     * @param c       类类型
     * @return 所有元素的类类型和c完全相同时返回true，如果数组中只有一个元素时也返回true，否则返回false
     * @since 0.5

    public static boolean isSameClassType(Object[] objects, Class c)
    {
    if (objects.length == 1) return true;
    for (int i = 1; i < objects.length; i++)
    {
    if (!c.equals(objects[i].getClass()))
    {
    return false;
    }
    }
    return true;
    }
     */
    /**
     * 判断child是否是c的一个子类。
     *
     * @param c     父类
     * @param child 要判断的可能的子类
     * @return child是c的子类的时候返回true，其他所有情况下都返回false
     * @since 0.5

    public static boolean isSubclass(Class c, Class child)
    {
    try
    {
    return c.isInstance(child.newInstance());
    } catch (IllegalAccessException e)
    {
    return false;
    } catch (InstantiationException ie)
    {
    return false;
    } catch (Exception oe)
    {
    return false;
    }
    }
     */
    /**
     * Create a new instance given a class name
     *
     * @param className A class name
     * @return A new instance
     * @throws ClassNotFoundException 不存在的类
     * @throws IllegalAccessException 步骤错误
     * @throws InstantiationException 错误
     */
    public static Object newInstance(String className) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
        return Class.forName(StringUtil.trim(className), true, getClassLoader()).newInstance();
    }


    /**
     * @param className 类名
     * @param arg       参数
     * @return 带参数的创建对象
     * @throws Exception 异常
     */
    @SuppressWarnings("unchecked")
    public static Object newInstance(String className, Object[] arg) throws Exception {
        if (arg == null) return newInstance(className);
        Class c = ClassUtil.loadClass(className);
        Class[] classArray = new Class[arg.length];
        for (int i = 0; i < arg.length; i++) {
            classArray[i] = arg[i].getClass();
        }
        Constructor c1 = c.getDeclaredConstructor(classArray);
        c1.setAccessible(true);
        return c1.newInstance(arg);
    }


    /**
     * Load a class given its name.
     * BL: We wan't to use a known ClassLoader--hopefully the heirarchy
     * is set correctly.
     *
     * @param className A class name
     * @return The class pointed to by <code>className</code>
     * @throws ClassNotFoundException If a loading error occurs
     */
    public static Class loadClass(String className) throws ClassNotFoundException {
        return Class.forName(StringUtil.trim(className), true, getClassLoader());
    }


    /**
     * @param resource 载入资源
     * @return URL 路径
     */
    public static URL getResource(String resource) {
        return getClassLoader().getResource(resource);
    }

    /**
     * Return the context classloader.
     * BL: if this is command line dbhand, the classloading issues
     * are more sane.  During upload execution, we explicitly set
     * the ClassLoader.
     *
     * @return The context classloader.
     */
    private static ClassLoader getClassLoader() {
        try {
            return Thread.currentThread().getContextClassLoader();
        } catch (Exception e) {
            return System.class.getClassLoader();
        }
    }

    /**
     * Determine the last modification date for this
     * class file or its enclosing library
     *
     * @param aClass A class whose last modification date is queried
     * @return The time the given class was last modified
     * @throws IllegalArgumentException The class was not loaded from a file
     *                                  or directory
     */
    public static long lastModified(Class aClass) throws IllegalArgumentException {
        URL url = aClass.getProtectionDomain().getCodeSource().getLocation();
        if (!url.getProtocol().equals("file")) {
            throw new IllegalArgumentException("Class was not loaded from a file url");
        }

        File directory = new File(url.getFile());
        if (!directory.isDirectory()) {
            throw new IllegalArgumentException("Class was not loaded from a directory");
        }
        String className = aClass.getName();
        String basename = className.substring(className.lastIndexOf(".") + 1);

        File file = new File(directory, basename + ".class");

        return file.lastModified();
    }

    /**
     * Gets the absolute pathname of the class file
     * containing the specified class name, as prescribed
     * by the current classpath.
     *
     * @param aClass Name of the class.
     */
    public static String which(Class aClass) {
        String path = null;
        try {
            path = aClass.getProtectionDomain().getCodeSource().getLocation().toString();
        } catch (Throwable t) {
            t.printStackTrace();
        }
        return path;
    }

    static public String getClassFilePath(String className) {
        if (className == null) return StringUtil.empty;
        if (className.equalsIgnoreCase("null")) return null;
        if (className.equals("")) return null;

        if (!className.startsWith("/")) className = "/" + className;
        className = className.replace('.', '/');
        className = className + ".class";
        java.net.URL classUrl = ClassUtil.class.getResource(className);
        if (classUrl != null) {
            return classUrl.getPath();
        }
        String classpath = System.getProperties().getProperty("java.class.path");
        String[] classPathList = classpath.split(StringUtil.SEMICOLON);
        String basePath;
        for (String path : classPathList) {
            if (!path.contains(".jar") || !path.contains(".zip") || !path.contains(".apk")) {
                basePath = FileUtil.mendPath(path);
                basePath = basePath.substring(0, basePath.length() - 1) + "!" + className;
                if (FileUtil.isFileExist(basePath)) {
                    return basePath;
                }
            }
        }
        return StringUtil.empty;
    }

    //------------------------------------------------------------------------------------------------------------------


    /**
     * @param f1 字段数组1
     * @param f2 字段数组2
     * @return 合并两个字段数组
     */
    public static Field[] addFieldArray(Field[] f1, Field[] f2) {
        if (f1 == null) return f2;
        if (f2 == null) return f1;
        Field[] result = new Field[f1.length + f2.length];
        System.arraycopy(f1, 0, result, 0, f1.length);
        System.arraycopy(f2, 0, result, f1.length, f2.length);
        return result;
    }

    /**
     * @param cls 类
     * @return cls的所有字段
     */
    public static Field[] getDeclaredFields(Class cls) {
        Class superclass = cls;
        Field[] result = null;
        while (!(superclass == null || superclass.equals(Object.class) || superclass.equals(Serializable.class))) {
            Field[] fields = superclass.getDeclaredFields();
            result = addFieldArray(result, fields);
            superclass = superclass.getSuperclass();
        }
        return result;
    }

    public static String[] getDeclaredFieldNames(Class cls) {
        Field[] fields = ClassUtil.getDeclaredFields(cls);
        Set<String> set = new LinkedHashSet<String>();
        for (Field field : fields) {
            set.add(field.getName());
        }
        return set.toArray(new String[0]);
    }


    public static Field getDeclaredField(Class cls, String fieldName) {
        if (fieldName == null) return null;
        Class childClass = cls;
        while (childClass != null) {
            if (childClass.equals(Object.class) || childClass.equals(Serializable.class)) break;
            Field[] fields = childClass.getDeclaredFields();
            for (Field f : fields) {
                if (fieldName.equals(f.getName())) return f;
            }
            childClass = childClass.getSuperclass();
        }
        return null;
    }

    /**
     * @param cls 类
     * @return cls 类的所有方法
     */
    public static Method[] getDeclaredMethods(Class cls) {
        Class childClass = cls;
        Method[] result = null;
        while (childClass != null) {
            if (childClass.equals(Object.class) || childClass.equals(Serializable.class)) break;
            result = BeanUtil.joinMethodArray(result, childClass.getDeclaredMethods());
            childClass = childClass.getSuperclass();
        }
        return result;
    }

    /**
     * @param cls  类
     * @param name 方法名称
     * @return 判断是否为这个类的方法
     */
    public static boolean isDeclaredMethod(Class cls, String name) {
        if (name == null) return false;
        Method[] methods = getDeclaredMethods(cls);
        for (Method method : methods) {
            if (method.getName().equals(name)) {
                return true;
            }
        }
        return false;
    }

    /**
     * @param cls  类
     * @param name 方法名称
     * @return 根据方法名称返回方法对象
     */
    public static Method getDeclaredMethod(Class cls, String name) {
        if (name == null) return null;
        Method[] methods = getDeclaredMethods(cls);
        for (Method method : methods) {
            if (method.getName().equals(name) || method.getName().equals(methodName_get + StringUtil.capitalize(name))
                    || method.getName().equals(methodName_set + StringUtil.capitalize(name))
                    || method.getName().equals(methodName_is + StringUtil.capitalize(name))) {
                return method;
            }
        }
        return null;
    }

    /**
     * @param cls  类
     * @param name 方法名称
     * @param pi   参数
     * @return 得到这个类根据方法名称，并且参数个数要相同
     */
    public static Method getDeclaredMethod(Class cls, String name, int pi) {
        if (name == null) return null;
        Method[] methods = getDeclaredMethods(cls);
        for (Method method : methods) {
            if ((method.getName().equals(name) || method.getName().equals(methodName_get + StringUtil.capitalize(name))
                    || method.getName().equals(methodName_set + StringUtil.capitalize(name))
                    || method.getName().equals(methodName_is + StringUtil.capitalize(name)))
                    && method.getParameterTypes().length == pi) {
                return method;
            }
        }
        return null;
    }

    /**
     * @param cls 类
     * @return 得到得到所有set的方法
     */
    public static Method[] getDeclaredSetMethods(Class cls) {
        Method[] methods = getDeclaredMethods(cls);
        Method[] result = null;
        for (Method method : methods) {
            if (method.getName().startsWith(methodName_set)) {
                result = BeanUtil.appendMethodArray(result, method);
            }
        }
        return result;
    }

    /**
     * @param cls 类
     * @return 得到所有get方法
     */
    public static Method[] getDeclaredReturnMethods(Class cls) {
        Method[] methods = getDeclaredMethods(cls);
        Method[] result = null;
        for (Method method : methods) {
            if (!method.getGenericReturnType().equals(Void.TYPE)) {
                result = BeanUtil.appendMethodArray(result, method);
            }
        }
        return result;
    }

    /**
     * @param method 方法  例如: setName  返回:name
     * @return 得到方法名称
     */
    public static String getCallMethodName(Method method) {
        if (method == null) return null;
        if (method.getName().startsWith(methodName_get) || method.getName().startsWith(methodName_set)) {
            return StringUtil.uncapitalize(method.getName().substring(3));
        }
        if (method.getName().startsWith(methodName_is)) {
            return StringUtil.uncapitalize(method.getName().substring(methodName_is.length()));
        }
        return method.getName();
    }


    public static boolean isArrayType(Type type) {
        return type.equals(String[].class) || type.equals(int[].class) || type.equals(Integer[].class) ||
                type.equals(long[].class) || type.equals(Long[].class) ||
                type.equals(float[].class) || type.equals(Float[].class) ||
                type.equals(double[].class) || type.equals(Double[].class) ||
                type.equals(char[].class) || type.equals(Character[].class) ||
                type.equals(byte[].class) || type.equals(BigInteger[].class) ||
                type.equals(BigDecimal[].class) || type.equals(Object[].class);
    }

    /**
     * Calls a static method
     *
     * @param aClass  类
     * @param aMethod 方法
     * @param args    参数
     * @return an object
     */
    @SuppressWarnings("unchecked")
    public static Object callStaticMethod(Class aClass, String aMethod, Object... args) {
        if (aClass == null) return null;
        try {
            Class[] argClasses = getClassArray(args);
            Method method;
            if (argClasses != null) {
                method = aClass.getMethod(aMethod, argClasses);
            } else {
                method = aClass.getMethod(aMethod);
            }
            if (method != null)
                return method.invoke(aClass, args);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }


    /**
     * @param className  类名
     * @param methodName 方法名
     * @param args       参数数组
     * @return 执行方法返回的结果
     * @throws Exception 没有类 运行错误
     */
    @SuppressWarnings("unchecked")
    public static Object invokeStaticMethod(String className, String methodName, Object[] args) throws Exception {
        Class ownerClass = Class.forName(className);
        Class[] argsClass = new Class[args.length];
        for (int i = 0, j = args.length; i < j; i++) {
            argsClass[i] = args[i].getClass();
        }
        return ownerClass.getMethod(methodName, argsClass).invoke(null, args);
    }

    /**
     * @param args 对象
     * @return 得到对象的类型数组
     */
    public static Class<?>[] getClassArray(Object[] args) {
        if (args == null) return null;
        Class[] argClasses = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            argClasses[i] = args[i].getClass();
        }

        return argClasses;
    }

}